using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.ServiceModel;

namespace VBoxService
{
    /// <summary>
    /// Service-Control-Interface.
    /// </summary>
    [ServiceContract]
    public interface IServiceStatus
    {
        [OperationContract]
        int getCurrentStatus();

        [OperationContract]
        bool setPowerMode(int iTargetMode);

        [OperationContract]
        bool mountDVD(bool bIsPhysical, string sTargetPath);

        [OperationContract]
        string getDriveStatus(string sController, string sPort, string sDevice);
    }

    /// <summary>
    /// VBox-Guest ServiceWrapper
    /// </summary>
    public class ServerWrapper : System.ServiceProcess.ServiceBase
    {
        /// <summary>
        /// Service-Configuration.
        /// </summary>
        private Settings oServiceSettings;

        /// <summary>
        /// Control-Connection Host.
        /// </summary>
        public ServiceHost WCFHost;

        /// <summary>
        /// Eventlog-Handler.
        /// </summary>
        private System.Diagnostics.EventLog oServiceEvents;

        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;

        /// <summary>
        /// Current Guest-Status (Powermode).
        /// </summary>
        public int CurrentStatus = 0;

        /// <summary>
        /// Initialize ServerWrapper-Instance.
        /// </summary>
        public ServerWrapper()
        {
            InitializeComponent();

            /*******************************************************************
             * Create a EventLog-Source for Message-Logging.
             * */
            if (!System.Diagnostics.EventLog.SourceExists("VBoxServiceSource"))
            {
                System.Diagnostics.EventLog.CreateEventSource("VBoxServiceSource", "VBoxService");
            }

            oServiceEvents.Source = "VBoxServiceSource";
            oServiceEvents.Log = "VBoxService";

            /*******************************************************************
             * Load Settings.
             * */
            oServiceSettings = new Settings();
            if (!oServiceSettings.Load())
            {
                oServiceEvents.WriteEntry("Settings not loaded. Service terminated!", EventLogEntryType.Error);
                //Environment.Exit(0);
            }

            /*******************************************************************
             * Setup Interprocess-Communication
             * */
            oServiceEvents.WriteEntry("Control-URL: net.tcp://localhost:" + oServiceSettings.ControlPort);
            WCFHost = new ServiceHost(typeof(ServiceStatus), new Uri[]{ new Uri("net.tcp://localhost:" + oServiceSettings.ControlPort) });
            WCFHost.AddServiceEndpoint(typeof(IServiceStatus), new NetTcpBinding(), "net.tcp://localhost:" + oServiceSettings.ControlPort);
            WCFHost.Open();

            oServiceEvents.WriteEntry("Control-URL: net.tcp://localhost:" + oServiceSettings.ControlPort + " established");
        }

        /// <summary>
        /// Service-Entry-Point.
        /// </summary>
        static void Main()
        {
            System.ServiceProcess.ServiceBase[] ServicesToRun;

            // More than one user Service may run within the same process. To add
            // another service to this process, change the following line to
            // create a second service object. For example,
            //
            //   ServicesToRun = New System.ServiceProcess.ServiceBase[] {new Service1(), new MySecondUserService()};
            //
            ServicesToRun = new System.ServiceProcess.ServiceBase[] { new VBoxService.ServerWrapper() };

            System.ServiceProcess.ServiceBase.Run(ServicesToRun);
        }

        /// <summary> 
        /// Add Designer-Support.
        /// </summary>
        /// <remarks>DO NOT MODIFY WITH CODE-EDITOR!!!
        /// Use Designer instead!</remarks>
        private void InitializeComponent()
        {
            this.oServiceEvents = new System.Diagnostics.EventLog();
            ((System.ComponentModel.ISupportInitialize)(this.oServiceEvents)).BeginInit();
            // 
            // oServiceEvents
            // 
            this.oServiceEvents.Log = "VBoxService";
            this.oServiceEvents.Source = "VBoxServiceSource";
            // 
            // Service1
            // 
            this.ServiceName = "VBoxService";
            ((System.ComponentModel.ISupportInitialize)(this.oServiceEvents)).EndInit();

        }

        /// <summary>
        /// Custom Disposal-Handling.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose(disposing);
        }

        Process pVBoxServer = new Process();

        /// <summary>
        /// Server-Startup-Routine.
        /// </summary>
        protected override void OnStart(string[] args)
        {
            try
            {
                ProcessStartInfo oStartInfo = new ProcessStartInfo(oServiceSettings.VBoxManagePath, "startvm \"" + oServiceSettings.VBoxGuestName + "\" --type headless");
                oStartInfo.UseShellExecute = true;
                oStartInfo.ErrorDialog = false;
                oStartInfo.CreateNoWindow = true;
                oStartInfo.RedirectStandardOutput = false;
                oStartInfo.RedirectStandardInput = false;

                pVBoxServer = System.Diagnostics.Process.Start(oStartInfo);

                oServiceEvents.WriteEntry("VBox Service Wrapper starting \"" + oServiceSettings.VBoxGuestName + "\"");
            }
            catch (Exception ex)
            {
                oServiceEvents.WriteEntry("Unable to start VBoxService: " + ex.Message, EventLogEntryType.Error);
            }
        }

        /// <summary>
        /// Stop Service and VBox-Guest.
        /// </summary>
        protected override void OnStop()
        {
            ProcessStartInfo oStartInfo = new ProcessStartInfo(oServiceSettings.VBoxManagePath, "controlvm \"" + oServiceSettings.VBoxGuestName + "\" savestate");
            oStartInfo.UseShellExecute = true;
            oStartInfo.ErrorDialog = false;
            oStartInfo.CreateNoWindow = true;
            oStartInfo.RedirectStandardOutput = false;
            oStartInfo.RedirectStandardInput = false;

            pVBoxServer = System.Diagnostics.Process.Start(oStartInfo);
            DateTime dtStartTime = DateTime.Now;
            while (pVBoxServer.HasExited == false)
            {
                double dTimeoutSeconds = DateTime.Now.Subtract(dtStartTime).TotalSeconds;
                if (dTimeoutSeconds > 90)
                {
                    oServiceEvents.WriteEntry("VBox Service Wrapper reached timeout-limit while stopping \"" + oServiceSettings.VBoxGuestName + "\"");
                    Environment.Exit(1);
                }
            }

            oServiceEvents.WriteEntry("VBox Service Wrapper stopped \"" + oServiceSettings.VBoxGuestName + "\"");
            Environment.Exit(0);
        }

        /// <summary>
        /// Pause Service & Virtual Machine.
        /// </summary>
        protected override void OnPause()
        {
            ProcessStartInfo oStartInfo = new ProcessStartInfo(oServiceSettings.VBoxManagePath, "controlvm \"" + oServiceSettings.VBoxGuestName + "\" pause");
            oStartInfo.UseShellExecute = true;
            oStartInfo.ErrorDialog = false;
            oStartInfo.CreateNoWindow = true;
            oStartInfo.RedirectStandardOutput = false;
            oStartInfo.RedirectStandardInput = false;

            pVBoxServer = System.Diagnostics.Process.Start(oStartInfo);
            DateTime dtStartTime = DateTime.Now;
            while (pVBoxServer.HasExited == false)
            {
                double dTimeoutSeconds = DateTime.Now.Subtract(dtStartTime).TotalSeconds;
                if (dTimeoutSeconds > 90)
                {
                    oServiceEvents.WriteEntry("VBox Service Wrapper reached timeout-limit while pausing \"" + oServiceSettings.VBoxGuestName + "\"");
                    Environment.Exit(1);
                }
            }

            oServiceEvents.WriteEntry("VBox Service Wrapper paused \"" + oServiceSettings.VBoxGuestName + "\"");
        }

        /// <summary>
        /// Resume paused Machine & Service.
        /// </summary>
        protected override void OnContinue()
        {
            ProcessStartInfo oStartInfo = new ProcessStartInfo(oServiceSettings.VBoxManagePath, "controlvm \"" + oServiceSettings.VBoxGuestName + "\" resume");
            oStartInfo.UseShellExecute = true;
            oStartInfo.ErrorDialog = false;
            oStartInfo.CreateNoWindow = true;
            oStartInfo.RedirectStandardOutput = false;
            oStartInfo.RedirectStandardInput = false;

            pVBoxServer = System.Diagnostics.Process.Start(oStartInfo);
            oServiceEvents.WriteEntry("VBox Service Wrapper resumed \"" + oServiceSettings.VBoxGuestName + "\"");
        }
    }
}
